#include "quiz.h"
#include <QMessageBox>

Quiz::Quiz(QObject *parent): QObject(parent)
{
}

Quiz::~Quiz() {
    qDeleteAll(questions.begin(), questions.end());
}

void Quiz::addQuestion(QString questionBody,
                       QList<QString> answers,
                       QList<bool> truths,
                       int score,
                       int penalty,
                       int ntanswers) {
    Question *newOne = new Question();
    if (answers.size() != truths.size()) {
        qWarning("Quiz::addQuestion: answers list has different size than truths list!");
        return;
    }

    if (score < 1) {
        qWarning("Quiz::addQuestion: score less than 1; defaulting to 1.");
        score = 1;
    }

    for (int i=0; i < answers.size(); i++) {
        newOne->answers.append(answers[i]);
        newOne->trueAnswers.append(truths[i]);
    }

    newOne->score = score;
    newOne->penalty = penalty;
    newOne->question = questionBody;
    newOne->ntAnswers = ntanswers;
    this->questions.append(newOne);
    this->activeQuestions.append(newOne);
}

int Quiz::saveToFile(QFile* file) {
    file->open(QFile::WriteOnly);
    QTextStream stream(file);
    for (int i=0;i<questions.size();i++) {
        Question* q = (Question*) questions[i];
        stream << i << ". " << q->question << '\n';
        stream << "## Score: " << q->score
               << " Penalty: " << q->penalty << '\n';
        for (int j=0;j<q->answers.size();j++) {
            if (q->trueAnswers[j])
                stream << '+';
            else
                stream << ' ';
            stream << "A. " << q->answers[j] << '\n';
        }
    }
    stream.flush();
    file->close();
    return 0;
}

int Quiz::loadFromFile(QFile *file, int score, int penalty, bool ignoreSession) {
    if (!file->open(QIODevice::ReadOnly | QIODevice::Text)) {
        qCritical("Quiz::loadFromFile: Unable to open file for reading.");
        return 1;
    }

    QTextStream stream(file);
    QRegExp questionRegExp("^\\d+[\\.\\)](.+)");
    QRegExp answerOk("^\\+\\D\\. (.+)");
    QRegExp answerBad("^(?: |-)\\D\\. (.+)");
    QRegExp sessionLine("^\\#\\#\\s+Score\\:\\s+(\\d+)\\s+Penalty\\:\\s+(\\d+)");
    QString str;

    if (!stream.atEnd())
        str = stream.readLine();

    while (true) {
        if (!str.contains(questionRegExp)) {
            // if it's not a question then ignore this line
            qDebug("Expecting a question, but found something different");

            // IMPORTANT! Ends loop when current string is not a question
            // and we are at the end of the stream.
            if (stream.atEnd())
                return 0;

            str = stream.readLine();
            continue;
        }

        QString questionBody = questionRegExp.cap(1);
        QList<QString> answers;
        int pen = penalty;
        int sco = score;
        int ntAnswers = 0;
        QList<bool> truths;

        // now let's read all the answers
        while (!stream.atEnd()) {
            str = stream.readLine();

            // is it a good answer?
            if (str.contains(answerOk)) {
                answers.append(answerOk.cap(1));
                truths.append(true);
                ntAnswers++;
            } else if (str.contains(answerBad)) {
                answers.append(answerBad.cap(1));
                truths.append(false);
            } else if (str.contains(sessionLine)) {
                if (!ignoreSession) {
                    bool ok;
                    sco = sessionLine.cap(1).toInt(&ok,10);
                    if (!ok) {
                        sco = score;
                        qWarning("Score saved in session line is bad, using default");
                    }
                    pen = sessionLine.cap(2).toInt(&ok,10);
                    if (!ok) {
                        pen = penalty;
                        qWarning("Penalty saved in session line is bad, using default");
                    }
                }
            } else
                break;
        }

        this->addQuestion(questionBody,answers,truths,sco,pen,ntAnswers);

        if (stream.atEnd())
            return 0;
    }
    return 0;
}

/**
  * Removes the question from the pool.
  */
void Quiz::removeQuestion(Question* toRemove) {
    this->activeQuestions.removeOne(toRemove);
}

/**
 * Returns a random question, that has positive score.
 * If it finds a question with score lower or equal to 0,
 * it removes it from the pool and tries to find another
 * one.
 * @return Question* of a random question from pool.
 */
Question* Quiz::getRandom() {
    Question* result;
    do {
        if (activeQuestions.isEmpty())
            return 0;
        result = activeQuestions[qrand() % activeQuestions.size()];
        if (result->score <= 0)
            removeQuestion(result);
    } while (result->score <= 0);
    return result;
}

/**
  * Cleans the pool by removing all of the questions with
  * score lower or equal 0.
  */
void Quiz::cleanup() {
    // TODO: there must be a better way to do it
    for (int i=0;i<questions.size();i++) {
        Question* q = (Question*) questions[i];
        if (q->score <= 0)
            removeQuestion(q);
    }
}
